/*
 * Copyright Aristool AG Switzerland
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 * 
 * http://www.apache.org/licenses/LICENSE-2.0 
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License.
 */
package org.opprua.tidy.util;

import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Attr;
import org.w3c.dom.CDATASection;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;

/**
 * TODO: add Java doc
 * 
 * Prepared for first commit: 15.04.2007
 * 
 * @author Lin Li
 */
public abstract class XReader {

  private String tag = "";
  private Map<String, String> attributes = new TreeMap<String, String>();
  private String text = "";

  public XReader() {
  }

  public abstract void read(Element e);

  public final void basic(Element e) {
    tag = e.getTagName();
    text = collectText(e);
    NamedNodeMap nm = e.getAttributes();
    for (int i = 0; i < nm.getLength(); i++) {
      Attr n = (Attr) nm.item(i);
      attributes.put(n.getName(), n.getValue());
    }
  }

  public String getTagName() {
    return tag;
  }

  public String getText() {
    return text;
  }

  public String[] getAttributes() {
    return (String[]) attributes.keySet().toArray(new String[0]);
  }

  public String getAttribute(String key) {
    String c = (String) attributes.get(key);
    if (c == null) { return ""; }
    return c;
  }

  public void setAttribute(String attr, String value) {
    if (value == null) {
      attributes.remove(attr);
    }
    attributes.put(attr, value);
  }

  public static String collectText(Element e) {
    StringBuffer buf = new StringBuffer();
    NodeList nl = e.getChildNodes();
    for (int i = 0; i < nl.getLength(); i++) {
      Node n = nl.item(i);
      String tt = "";
      if (n.getNodeType() == Node.TEXT_NODE) {
        tt = ((Text) n).getData();
      } else if (n.getNodeType() == Node.CDATA_SECTION_NODE) {
        tt = ((CDATASection) n).getData();
      }
      tt = tt.trim();
      if (tt.length() > 0) {
        if (buf.length() > 0) {
          buf.append("\n");
        }
        buf.append(tt);
      }
    }
    return buf.toString();
  }

  public static String collectTextForCopy(Element e) {
    StringBuffer buf = new StringBuffer();
    NodeList nl = e.getChildNodes();
    for (int i = 0; i < nl.getLength(); i++) {
      Node n = nl.item(i);
      String tt = "";
      if (n.getNodeType() == Node.TEXT_NODE) {
        tt = ((Text) n).getData();
      } else if (n.getNodeType() == Node.CDATA_SECTION_NODE) {
        tt = "<![CDATA[" + ((CDATASection) n).getData() + "]]>";
      }
      tt = tt.trim();
      if (tt.length() > 0) {
        buf.append(tt).append("\n");
      }
    }
    return buf.toString();
  }

  @SuppressWarnings("unchecked")
  public static <T extends XReader> T[] collect(Element e, String tagName, Class<T> type) {
    NodeList n = e.getChildNodes();
    List<XReader> l = new ArrayList<XReader>();
    for (int i = 0; i < n.getLength(); ++i) {
      if (n.item(i).getNodeType() != Node.ELEMENT_NODE
                  || !((Element) n.item(i)).getTagName().equals(tagName)) {
        continue;
      }
      Element r = (Element) n.item(i);
      try {
        XReader s = (XReader) type.newInstance();
        s.basic(r);
        s.read(r);
        l.add(s);
      } catch (Throwable ex) {
        throw new Error("Failed to collect into class: " + type, ex);
      }
    }

    return l.toArray((T[]) Array.newInstance(type, l.size()));
  }

  public static <T extends XReader> T collectOne(Element e, String tagName, Class<T> type) {
    T[] s = collect(e, tagName, type);
    if (s.length == 1) { return s[0]; }
    return null;
  }

  public static Element getDocRoot(String fileName) {
    try {
      return getDocRoot(new FileInputStream(fileName));
    } catch (Throwable x) {
      throw new Error("Failed to construct DOM from file " + fileName, x);
    }
  }

  public static Element getDocRoot(InputStream file) {
    try {
      return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(file)
                  .getDocumentElement();
    } catch (Throwable x) {
      throw new Error("Failed to construct DOM from inputstream ", x);
    }
  }

  public static String pick(Element e, String attributeName, String defaultValue) {
    String s = e.getAttribute(attributeName);
    if (s == null || s.trim().length() == 0) {
      s = defaultValue;
    }
    return s;
  }

}